#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include "textfile.h"
#include "glm.h"
#include "material.h"
#include <map>
#include <float.h>

#include "dirent.h"
#include "natewin.h"

#define LEFT 0
#define RIGHT 1

GLfloat xRot=0, yRot=0;
float lpos[4] = {1.0,1.0,1.0,0};
int old_y;

int diffmod_invert = 0;
int diffmod_stretch = 0;
int diffmod_grey = 0;

GLfloat pos[4] = { 5.0, 5.0, 5.0, 0.0 };

GLMmodel* pmodel = NULL;

GLdouble projection[16], modelview[16], inverse[16]; 

void renderDiff();

cell lookat[9] = {
    { 1, 180, 120, -5.0, 5.0, 0.0, 0.1,
        "Specifies the X position of the eye point.", "%.2f" },
    { 2, 240, 120, -5.0, 5.0, 0.0, 0.1,
    "Specifies the Y position of the eye point.", "%.2f" },
    { 3, 300, 120, -5.0, 5.0, 2.0, 0.1,
    "Specifies the Z position of the eye point.", "%.2f" },
    { 4, 180, 160, -5.0, 5.0, 0.0, 0.1,
    "Specifies the X position of the reference point.", "%.2f" },
    { 5, 240, 160, -5.0, 5.0, 0.0, 0.1,
    "Specifies the Y position of the reference point.", "%.2f" },
    { 6, 300, 160, -5.0, 5.0, 0.0, 0.1,
    "Specifies the Z position of the reference point.", "%.2f" },
    { 7, 180, 200, -2.0, 2.0, 0.0, 0.1,
    "Specifies the X direction of the up vector.", "%.2f" },
    { 8, 240, 200, -2.0, 2.0, 1.0, 0.1,
    "Specifies the Y direction of the up vector.", "%.2f" },
    { 9, 300, 200, -2.0, 2.0, 0.0, 0.1,
    "Specifies the Z direction of the up vector.", "%.2f" },
};

cell perspective[4] = {
    { 10, 180, 80, 1.0, 179.0, 60.0, 1.0,
        "Specifies field of view angle (in degrees) in y direction.", "%.1f" },
    { 11, 240, 80, -3.0, 3.0, 1.0, 0.01,
    "Specifies field of view in x direction (width/height).", "%.2f" },
    { 12, 300, 80, 0.1, 10.0, 1.0, 0.05,
    "Specifies distance from viewer to near clipping plane.", "%.1f" },
    { 13, 360, 80, 0.1, 10.0, 10.0, 0.05,
    "Specifies distance from viewer to far clipping plane.", "%.1f" },
};

void redisplay_all(void);

/* Window handlers */
GLuint winMain, 
       winLeft, 
	   winWorld,
	   winRight,
	   winDiff,
	   winCommand;

/* Menu */
GLuint menuShaders,
       menuMain,
	   menuModels;

/* Default subwindow sizes */
GLuint sub_width = 256, sub_height = 256;

using namespace std;

#define SHADER_DIR "../shaders/"
#define SHADER_DEFAULT1 "phong"
#define SHADER_DEFAULT2 "dirLightAmbDiffSpec"

typedef struct _shaderInfo {
	string name;
	string desc;
} TShaderInfo;

typedef map<int, TShaderInfo> TShaderMap;
TShaderMap shaderMap;

/* Maps window IDs to shaderInfo */
TShaderMap activeShaders;

#define MODEL_DIR "../models/"
#define MODEL_DEFAULT "rose+vase"

typedef struct _modelInfo {
	string name;
	string desc;
} TModelInfo;

typedef map<int, TModelInfo> TModelMap;
TModelMap modelMap;
void setModel(string name);

string shaders[] = {"",""};


void redisplay_all(void);
GLuint window, world, screen, command;

void main_reshape(int width,  int height)  {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, width, height, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
	#define GAP  25             /* gap between subwindows */
    sub_width = (width-GAP*4)/3.0;
    sub_height = (height-GAP*3)/3.0;
    
    glutSetWindow(winLeft);
    glutPositionWindow(GAP, GAP);
    glutReshapeWindow(sub_width, sub_height);

    glutSetWindow(winRight);
    glutPositionWindow(GAP+sub_width+GAP, GAP);
    glutReshapeWindow(sub_width, sub_height);

	glutSetWindow(winDiff);
    glutPositionWindow((GAP+sub_width)*2+GAP, GAP);
    glutReshapeWindow(sub_width, sub_height);

	glutSetWindow(winWorld);
    glutPositionWindow((GAP+sub_width)*2+GAP, GAP+sub_height+GAP);
    glutReshapeWindow(sub_width, sub_height);

    glutSetWindow(winCommand);
    glutPositionWindow(GAP, GAP+sub_height+GAP);
    glutReshapeWindow(sub_width+GAP+sub_width, sub_height*2);
}

void main_display(void)
{
    glClearColor(0.8, 0.8, 0.8, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3ub(0, 0, 0);
    setfont("helvetica", 12);
    drawstr(GAP, GAP-5, "Reflection window 1");
    drawstr(GAP+sub_width+GAP, GAP-5, "Reflection window 2");
    drawstr(GAP, GAP+sub_height+GAP-5, "Parameter manipulation window");
    glutSwapBuffers();
}

void main_keyboard(unsigned char key, int x, int y) {

	switch(key) {
		case GLUT_KEY_RIGHT:
			xRot += 2.0;
			if(xRot>360) xRot=0;
			break;

		case GLUT_KEY_LEFT:
			xRot -= 2.0;
			if(xRot<-360) xRot=0;
			break;

		case GLUT_KEY_UP:
			yRot += 2.0;
			if(yRot>360) yRot=0;
			break;

		case GLUT_KEY_DOWN:
			yRot -= 2.0;
			if(yRot<-360) yRot=0;
			break;

		case 'a':

			perspective[0].value = 17.0;
			perspective[1].value = 1.0;
			perspective[2].value = 1.0;
			perspective[3].value = 10.0;

			lookat[0].value = -0.9;
			lookat[1].value = 0.8;
			lookat[2].value = 2.0;
			lookat[3].value = 0.0;
			lookat[4].value = 0.6;
			lookat[5].value = 0.0;
			lookat[6].value = -0.9;
			lookat[7].value = 2.0;
			lookat[8].value = 0.0; 
			xRot=0;yRot=0;
			redisplay_all();
			break;

		case 'r':
			perspective[0].value = 60.0;
			perspective[1].value = 1.0;
			perspective[2].value = 1.0;
			perspective[3].value = 10.0;
			lookat[0].value = 0.0;
			lookat[1].value = 0.0;
			lookat[2].value = 2.0;
			lookat[3].value = 0.0;
			lookat[4].value = 0.0;
			lookat[5].value = 0.0;
			lookat[6].value = 0.0;
			lookat[7].value = 1.0;
			lookat[8].value = 0.0;
			xRot=0;yRot=0;
			redisplay_all();
			break;

		case 'x':
			renderDiff();
			break;
		
		case 'c':
			diffmod_invert = !diffmod_invert;
			renderDiff();
			break;
		
		case 'v':
			diffmod_stretch = !diffmod_stretch;
			renderDiff();
			break;

		case 'b':
			diffmod_grey = !diffmod_grey;
			renderDiff();
			break;

		case 27:
			exit(0);
	}
}

void main_speckeys(int key, int x, int y) {
	main_keyboard((unsigned char)key, x, y);
}

void command_reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, width, height, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glClearColor(0.0, 0.0, 0.0, 0.0);
}

void command_display(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glColor3ub(255,255,255);

	setfont("helvetica", 18);

	drawstr(10,17,"Left: %s\n", activeShaders[winLeft].name.c_str());
	drawstr(sub_width+10+GAP,17,"Right: %s\n", activeShaders[winRight].name.c_str());

	drawstr(180, perspective[0].y-40, "fovy"); 
	drawstr(230, perspective[0].y-40, "aspect"); 
	drawstr(300, perspective[0].y-40, "zNear"); 
	drawstr(360, perspective[0].y-40, "zFar");

	drawstr(40, perspective[0].y, "gluPerspective(");
	drawstr(230, perspective[0].y, ","); 
	drawstr(290, perspective[0].y, ",");
	drawstr(350, perspective[0].y, ",");
	drawstr(410, perspective[0].y, ");");

	drawstr(78, lookat[0].y, "gluLookAt(");
	drawstr(230, lookat[0].y, ","); 
	drawstr(290, lookat[0].y, ",");
	drawstr(350, lookat[0].y, ",");
	drawstr(380, lookat[0].y, "<- eye");
	drawstr(230, lookat[3].y, ","); 
	drawstr(290, lookat[3].y, ",");
	drawstr(350, lookat[3].y, ",");
	drawstr(380, lookat[3].y, "<- center");
	drawstr(230, lookat[6].y, ","); 
	drawstr(290, lookat[6].y, ",");
	drawstr(350, lookat[6].y, ");");
	drawstr(380, lookat[6].y, "<- up");

	cell_draw(&perspective[0]);
	cell_draw(&perspective[1]);
	cell_draw(&perspective[2]);
	cell_draw(&perspective[3]); 

	cell_draw(&lookat[0]);
	cell_draw(&lookat[1]);
	cell_draw(&lookat[2]);
	cell_draw(&lookat[3]);
	cell_draw(&lookat[4]);
	cell_draw(&lookat[5]);
	cell_draw(&lookat[6]);
	cell_draw(&lookat[7]);
	cell_draw(&lookat[8]);

	if (!getSelection()) {
		glColor3ub(255, 255, 0);
		drawstr(10, 240,
			"Click on the arguments and move the mouse to modify values.");
	}   

	glutSwapBuffers(); 
}


void commandRefresh() {
	glutSetWindow(winCommand);
	command_display();
}



void command_mouse(int button, int state, int x, int y) {
	GLint selection = 0;

	if (state == GLUT_DOWN) {
		/* mouse should only hit _one_ of the cells, so adding up all
		the hits just propagates a single hit. */
		selection += cell_hit(&perspective[0], x, y);
		selection += cell_hit(&perspective[1], x, y);
		selection += cell_hit(&perspective[2], x, y);
		selection += cell_hit(&perspective[3], x, y);

		selection += cell_hit(&lookat[0], x, y);
		selection += cell_hit(&lookat[1], x, y);
		selection += cell_hit(&lookat[2], x, y);
		selection += cell_hit(&lookat[3], x, y);
		selection += cell_hit(&lookat[4], x, y);
		selection += cell_hit(&lookat[5], x, y);
		selection += cell_hit(&lookat[6], x, y);
		selection += cell_hit(&lookat[7], x, y);
		selection += cell_hit(&lookat[8], x, y);
	}

	old_y = y;
	setSelection(selection);
	redisplay_all();
}

void command_motion(int x, int y) {
    cell_update(&perspective[0], old_y-y);
    cell_update(&perspective[1], old_y-y);
    cell_update(&perspective[2], old_y-y);
    cell_update(&perspective[3], old_y-y);
    cell_update(&lookat[0], old_y-y);
    cell_update(&lookat[1], old_y-y);
    cell_update(&lookat[2], old_y-y);
    cell_update(&lookat[3], old_y-y);
    cell_update(&lookat[4], old_y-y);
    cell_update(&lookat[5], old_y-y);
    cell_update(&lookat[6], old_y-y);
    cell_update(&lookat[7], old_y-y);
    cell_update(&lookat[8], old_y-y);
    
    old_y = y;
    
    redisplay_all();
}

void command_menu(int value) {
    main_keyboard((unsigned char)value, 0, 0);
}

void SetupRC()
{
	glEnable(GL_DEPTH_TEST);
	glClearColor(1.0,1.0,1.0,1.0);
	gluLookAt(0.0,0.0,5.0, 
		      0.0,0.0,-1.0,
			  0.0f,1.0f,0.0f);
}

void renderSceneDo()
{

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//glLoadIdentity();

	glPushMatrix();

	glLightfv(GL_LIGHT0, GL_POSITION, pos);

	glRotatef(yRot,1,0,0);
	glRotatef(xRot,0,1,0);


	//
	//glLightfv(GL_LIGHT0, GL_SPECULAR, lpos);

	if (!pmodel) {
        setModel(MODEL_DEFAULT);
    }
    
	//setMaterial(GL_FRONT_AND_BACK, 30);
    glmDraw(pmodel, GLM_SMOOTH | GLM_MATERIAL);


	glPopMatrix();

	GLuint win = glutGetWindow();
	
	glutSwapBuffers();
}

void renderSceneDoWorld()
{

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//glLoadIdentity();

	glPushMatrix();

	glColor3ub(255, 255, 255);
        glBegin(GL_LINE_STRIP);
        glVertex3f(0.0, 0.0, 0.0);
        if (pos[3] == 0)        /* 10.0 = 'infinite' light */
            glVertex3f(pos[0]*10.0,pos[1]*10.0,pos[2]*10.0);
        else 
            glVertex3f(pos[0], pos[1], pos[2]);
        glEnd();
        glLightfv(GL_LIGHT0, GL_POSITION, pos);

	glRotatef(yRot,1,0,0);
	glRotatef(xRot,0,1,0);


	

	//
	//glLightfv(GL_LIGHT0, GL_SPECULAR, lpos);

	if (!pmodel) {
        setModel(MODEL_DEFAULT);
    }
    
	//setMaterial(GL_FRONT_AND_BACK, 30);

    glScalef(0.3,0.3,0.3);

	glmDraw(pmodel, GLM_SMOOTH | GLM_MATERIAL);

	glPopMatrix();

	GLuint win = glutGetWindow();
	
	glutSwapBuffers();
}

void renderDiff() {
   float* leftBuffer  = new float[sub_width*sub_height*4];
   float* rightBuffer = new float[sub_width*sub_height*4];
   float* diffBuffer  = new float[sub_width*sub_height*4];
   
   glutSetWindow(winLeft);
   //glutSwapBuffers();
   glPixelStorei(GL_PACK_ALIGNMENT, 4);
   glReadPixels((GLint)0, (GLint)0, (GLint)sub_width, (GLint)sub_height, GL_BGRA, GL_FLOAT, leftBuffer);

   glutSetWindow(winRight);
   //glutSwapBuffers();
   glPixelStorei(GL_PACK_ALIGNMENT, 4);
   glReadPixels((GLint)0, (GLint)0, (GLint)sub_width, (GLint)sub_height, GL_BGRA, GL_FLOAT, rightBuffer);

   float* min = new float[3];
   float* max = new float[3];
  
   for(int c = 0; c < 3; c++) {
	   min[c]=FLT_MAX;
	   max[c]=FLT_MIN;
   }

   for(int i = 0; i < sub_width*sub_height; i++) {
	   for(int c = 0; c < 3; c++) {
		   diffBuffer[i*4+c] = abs(leftBuffer[i*4+c]-rightBuffer[i*4+c]);
		   if(diffBuffer[i*4+c] > max[c]) max[c] = diffBuffer[i*4+c];
		   if(diffBuffer[i*4+c] < min[c]) min[c] = diffBuffer[i*4+c];
	   }
	   diffBuffer[i*4+3] = 1.0f;
   }   
   
   
   if(diffmod_grey) {
	   for(int c = 0; c < 3; c++) {
		   min[c]=FLT_MAX;
		   max[c]=FLT_MIN;
	   }
	   for(int i = 0; i < sub_width*sub_height; i++) {
		   for(int c = 0; c < 3; c++) {
			   diffBuffer[i*4+c] = 0.3*diffBuffer[i*4+0] + 0.59*diffBuffer[i*4+1] + 0.11*diffBuffer[i*4+2];
			   if(diffBuffer[i*4+c] > max[c]) max[c] = diffBuffer[i*4+c];
			   if(diffBuffer[i*4+c] < min[c]) min[c] = diffBuffer[i*4+c];
		   }
	   }
   }

   if(diffmod_stretch) {
	   float* wid = new float[3];
	   for(int c = 0; c < 3; c++) {
		   wid[c] = max[c]-min[c];
	   }
	   for(int i = 0; i < sub_width*sub_height; i++) {
		   for(int c = 0; c < 3; c++) {
			   diffBuffer[i*4+c] = (diffBuffer[i*4+c] - min[c]) / wid[c];
		   }
	   }
   }

   if(diffmod_invert) {
	   for(int i = 0; i < sub_width*sub_height*4; i++) {
		   diffBuffer[i] = 1.0f-diffBuffer[i];
	   }
   }

   glutSetWindow(winDiff);
   
   glEnable(GL_DEPTH_TEST);
   glClearColor(0.0, 1.0, 1.0, 1.0);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 0);
   glDrawPixels(sub_width,sub_height,GL_BGRA,GL_FLOAT,diffBuffer);
   glutSwapBuffers();

   printf("Render diff.\n");
}
void renderScene() {
	glutSetWindow(winRight);
	renderSceneDo();
	glutSetWindow(winLeft);
	renderSceneDo();
	glutSetWindow(winWorld);
	renderSceneDoWorld();
}

void reshapeScene(int width, int height)
{
	/*
	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if(h == 0)
		h = 1;

	float ratio = 1.0* w / h;

	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	// Set the viewport to be the entire window
    glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45,ratio,1,1000);
	glMatrixMode(GL_MODELVIEW);
	*/

	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(perspective[0].value, perspective[1].value, 
		perspective[2].value, perspective[3].value);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(lookat[0].value, lookat[1].value, lookat[2].value,
		lookat[3].value, lookat[4].value, lookat[5].value,
		lookat[6].value, lookat[7].value, lookat[8].value);
	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glClearColor(0.2, 0.2, 0.2, 0.0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0); 
}

void redisplay_all(void)
{
    glutSetWindow(winCommand);
    command_display();
	glutPostRedisplay();
    
	glutSetWindow(winRight);
	renderSceneDo();
	reshapeScene(sub_width, sub_height);
    glutPostRedisplay();

	glutSetWindow(winLeft);
    renderSceneDo();
	reshapeScene(sub_width, sub_height);
	glutPostRedisplay();

	glutSetWindow(winWorld);
    renderSceneDoWorld();
	reshapeScene(sub_width, sub_height);
	glutPostRedisplay();
}


void setShaders(string name) {

	GLuint vertex,
		   fragment,
		   program;

	printf("before glCreateShader\n");
	
	

	vertex = glCreateShader(GL_VERTEX_SHADER);
	fragment = glCreateShader(GL_FRAGMENT_SHADER);
	name.insert(0,SHADER_DIR);

	const char * txtVertex   = textFileRead(name.substr(0,name.size()).append(".vert").c_str());
	const char * txtFragment = textFileRead(name.substr(0,name.size()).append(".frag").c_str());

	glShaderSource(vertex, 1, &txtVertex, NULL);
	glShaderSource(fragment, 1, &txtFragment, NULL);

	glCompileShader(vertex);
	glCompileShader(fragment);

	program = glCreateProgram();
	glAttachShader(program,fragment);
	glAttachShader(program,vertex);

	glLinkProgram(program);
	glUseProgram(program);

}


void menuMainFunc(int value) {
	main_keyboard((unsigned char)value,0,0);

}

void loadModelMap() {
	modelMap.clear();
	DIR *dir;
	struct dirent *ent;
	int i = 0;

	/* open directory stream */
	dir = opendir (MODEL_DIR);
	if (dir != NULL) {

		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			string _name = ent->d_name;
			size_t pos = _name.rfind(".");
			
			if(_name.substr(pos+1,3).compare("obj") == 0) {
				modelMap[++i] = TModelInfo();
				modelMap[i].name = _name.substr(0, pos);
				modelMap[i].desc = ""; //textFileRead(name.substr(0,name.size()).insert(0,SHADER_DIR).append("desc").c_str());
			}
			
		}
		closedir (dir);
	} else {
		/* could not open directory */
		perror ("Error opening directory of shaders!");
		return;
	}
}

void menuShadersFunc(int value) { 
	TShaderMap::iterator it = shaderMap.find(value); 
	if(it != shaderMap.end()) { 
		string name = (it->second).name; 
		printf("Set shader: %s\n", name.c_str()); 
		GLuint winActive = glutGetWindow(); 
		activeShaders[winActive] = it->second; 
		setShaders(name); 
		commandRefresh(); 
	} 
} 

void setModel(string name) {
	pmodel = glmReadOBJ(name.insert(0,MODEL_DIR).append(".obj").c_str());
	if (!pmodel) exit(0);
	glmUnitize(pmodel);
	glmFacetNormals(pmodel);
	glmVertexNormals(pmodel, 90.0);

}

void menuModelsFunc(int value) {
	TModelMap::iterator it = modelMap.find(value);
	if(it != modelMap.end()) {
		string name = (it->second).name;
		printf("Set model: %s\n", name.c_str());
		setModel(name);
		commandRefresh();
	}
}

void loadShaderMap() {
	shaderMap.clear();
	DIR *dir;
	struct dirent *ent;
	int i = 0;

	/* open directory stream */
	dir = opendir (SHADER_DIR);
	if (dir != NULL) {

		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			string _name = ent->d_name;
			size_t pos = _name.rfind(".");
			
			if(_name.substr(pos+1,4).compare("desc") == 0) {
				shaderMap[++i] = TShaderInfo();
				shaderMap[i].name = _name.substr(0, pos);
				shaderMap[i].desc = ""; //textFileRead(name.substr(0,name.size()).insert(0,SHADER_DIR).append("desc").c_str());
			}
			
		}
		closedir (dir);
	} else {
		/* could not open directory */
		perror ("Error opening directory of shaders!");
		return;
	}
}

GLuint createShaderSubMenu() {

	GLuint _submenu = glutCreateMenu(menuShadersFunc);
		
	if(shaderMap.size() == 0) loadShaderMap();

	TShaderMap::iterator it;
	for (it = shaderMap.begin(); it != shaderMap.end(); it++ )
		glutAddMenuEntry( (*it).second.name.c_str() , (*it).first );

	return _submenu;
}


GLuint createModelsSubMenu() {

	GLuint _submenu = glutCreateMenu(menuModelsFunc);
		
	if(modelMap.size() == 0) loadModelMap();

	TModelMap::iterator it;
	for (it = modelMap.begin(); it != modelMap.end(); it++ )
		glutAddMenuEntry( (*it).second.name.c_str() , (*it).first );

	return _submenu;
}

GLuint createSubWindow(GLuint parent, GLint x, GLint y, GLint width, GLint height) {
	GLuint screen = glutCreateSubWindow(parent, x, y, width, height);
	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	glutReshapeFunc(reshapeScene);
	glutKeyboardFunc(main_keyboard);
	glutSpecialFunc(main_speckeys);

	glutCreateMenu(menuMainFunc);
	glutAddSubMenu("Shaders", menuShaders);
	glutAddSubMenu("Models", menuModels);
	glutAddMenuEntry("Reference view [a]", 'a');
	glutAddMenuEntry("Reset view [r]", 'r');
	glutAddMenuEntry("Make diff [x]", 'x');
	glutAddMenuEntry("Set diff inverse [c]", 'c');
	glutAddMenuEntry("Strech diff [v]", 'v');
	glutAddMenuEntry("Diff to grey [b]", 'b');


	glutAttachMenu(GLUT_RIGHT_BUTTON);

	SetupRC();
	return screen;
}

int main(int argc, char **argv)
{
	glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
    glutInitWindowSize(768+GAP*4, 768+GAP*3);
    glutInitWindowPosition(50, 50);
    glutInit(&argc, argv);
    
    winMain = glutCreateWindow("Reflection");
	glutReshapeFunc(main_reshape);
    glutDisplayFunc(main_display);
    glutKeyboardFunc(main_keyboard);
    glutSpecialFunc(main_speckeys);
	glewInit();
	if (glewIsSupported("GL_VERSION_2_0"))
		printf("Ready for OpenGL 2.0\n");
	else {
		printf("OpenGL 2.0 not supported\n");
		exit(1);
	}

	menuShaders = createShaderSubMenu();
	menuModels  = createModelsSubMenu();

	winLeft  = createSubWindow(winMain, GAP, GAP, 256, 256);
	setShaders(SHADER_DEFAULT1);
    winRight = createSubWindow(winMain, GAP+256+GAP, GAP, 256, 256);
	setShaders(SHADER_DEFAULT2);
	winWorld = createSubWindow(winMain, (GAP+256)*2+GAP, GAP+256+GAP, 256, 256);
	winDiff = glutCreateSubWindow(winMain, (GAP+256)*2+GAP, GAP, 256, 256);
    glutDisplayFunc(renderDiff);
	
	winCommand = glutCreateSubWindow(winMain, GAP+256+GAP, GAP+256+GAP, 256, 256);
    glutReshapeFunc(command_reshape);
    glutDisplayFunc(command_display);
    glutMotionFunc(command_motion);
    glutMouseFunc(command_mouse);
    glutKeyboardFunc(main_keyboard);
    glutSpecialFunc(main_speckeys);
	redisplay_all();

    glutMainLoop();
    
    return 0;
}

